package com.liang.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.liang.auth.domain.bo.AuthUserBO;
import com.liang.auth.domain.constants.AuthConstants;
import com.liang.auth.domain.convert.AuthUserBOConverter;
import com.liang.auth.domain.service.AuthRolePermissionDomainService;
import com.liang.auth.domain.service.AuthUserDomainService;
import com.liang.auth.infra.basic.entity.*;
import com.liang.auth.infra.basic.service.*;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    private final AuthUserService authUserService;
    private final AuthRoleService authRoleService;
    private final AuthUserRoleService authUserRoleService;
    private final AuthPermissionService authPermissionService;
    private final AuthRolePermissionService authRolePermissionService;
    private final RedisTemplate<String, Object> redisTemplate;

    private String salt = "jinitaimei";
    private String authPermissionPrefix = "auth:permission:";
    private String authRolePrefix = "auth:role:";
    private static final String LOGIN_PREFIX = "login:";

    @Override
    @SneakyThrows
    @Transactional
    public void register(AuthUserBO authUserBO) {
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBO2PO(authUserBO);
        if (StrUtil.isNotBlank(authUserBO.getPassword())) {
            authUser.setPassword(SaSecureUtil.md5BySalt(authUserBO.getPassword(), salt));
        }
        log.info("AuthUserDomainServiceImpl.register.po:{}", JSONUtil.toJsonStr(authUser));
        authUserService.save(authUser);
        // 建立一个初步的角色关联
        AuthRole authRole = authRoleService.getByRoleKey(AuthConstants.NORMAL_USER);
        Long roleId = authRole.getId();
        Long userId = authUser.getId();
        AuthUserRole authUserRole = AuthUserRole.builder().roleId(roleId).userId(userId).build();
        authUserRoleService.save(authUserRole);
        // 把当前用户的角色和权限刷新到Redis中
        String roleKey = authRolePrefix + authUser.getUsername();
        List<AuthRole> authRoleList = new ArrayList<>();
        authRoleList.add(authRole);
        redisTemplate.opsForValue().set(roleKey, JSONUtil.toJsonStr(authRoleList));
        // 刷新权限
        List<AuthRolePermission> list = authRolePermissionService.getByRoleId(roleId);
        List<Long> permissionIdList = list.stream()
                .map(AuthRolePermission::getPermissionId)
                .collect(Collectors.toList());
        List<AuthPermission> permissionList = authPermissionService.listByIds(permissionIdList);
        String permissionKey = authPermissionPrefix + authUser.getUsername();
        redisTemplate.opsForValue().set(permissionKey, JSONUtil.toJsonStr(permissionList));
    }

    @Override
    public void update(AuthUserBO authUserBO) {
        log.info("AuthUserDomainServiceImpl.update.bo:{}", JSONUtil.toJsonStr(authUserBO));
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBO2PO(authUserBO);
        log.info("AuthUserDomainServiceImpl.update.po:{}", JSONUtil.toJsonStr(authUser));
        AuthUser userInfo = authUserService.getUserInfo(authUser);
        authUser.setId(userInfo.getId());
        authUserService.updateById(authUser);
        // 由任何的更新，都要在缓存进行同步的更改
    }

    @Override
    public void removeById(Long id) {
        log.info("当前删除用户id：{}", id);
        authUserService.removeById(id);
    }

    @Override
    @Transactional
    public SaTokenInfo doLogin(String verificationCode) {
        String key = LOGIN_PREFIX + verificationCode;
        String openId = (String) redisTemplate.opsForValue().get(key);
        log.info("当前openId：{}", openId);
        // 判断用户是否存在，不存在先注册再登录
        Integer count = authUserService.countByOpenId(openId);
        if (count == 0) {
            AuthUserBO authUserBO = new AuthUserBO();
            authUserBO.setUsername(openId);
            register(authUserBO);
        }
        StpUtil.login(openId);
        return StpUtil.getTokenInfo();
    }

    @Override
    public AuthUserBO getUserInfo(AuthUserBO authUserBO) {
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBO2PO(authUserBO);
        AuthUser res = authUserService.getUserInfo(authUser);
        return AuthUserBOConverter.INSTANCE.convertPO2BO(res);
    }

    @Override
    public List<AuthUserBO> listUserInfoByNames(List<String> usernameList) {
        List<AuthUser> userList = authUserService.getByUsernames(usernameList);
        if (CollUtil.isEmpty(userList)) {
            return Collections.emptyList();
        }
        return AuthUserBOConverter.INSTANCE.convert2BOList(userList);
    }
}
